package spring;

import spring.aop.annotation.After;
import spring.aop.annotation.Aspect;
import spring.aop.annotation.Before;
import spring.aop.annotation.PointCut;
import spring.annotation.bean.AutoWired;
import spring.aop.utils.AopMatch;
import spring.aop.utils.AopMatchContext;
import spring.bean.BeanDefinition;
import spring.bean.BeanDefinitionReader;
import spring.aop.AopProcessor;
import spring.processor.BeanPostProcessor;
import spring.processor.InitializingBean;
import spring.aop.proxy.CglibProxy;
import spring.aop.proxy.JdkDynamicProxy;
import spring.utils.BeanUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @author cg
 * @date 2023/6/14 14:44
 */
public class ApplicationContext {
    //注册配置信息
    private Class<?> configClazz;
    /**
     * 注册配置类解析器
     */
    private BeanDefinitionReader reader;
    /**
     * 存放所有的bean对象原始信息
     */
    private Map<String, BeanDefinition> beanDefinitionMap;

    /**
     * 存放beanPostProcessor集合，自定义bean的前后处理逻辑
     */
    private CopyOnWriteArrayList<BeanPostProcessor> beanPostProcessors = new CopyOnWriteArrayList<>();
    /**
     * 存放aopProcessor集合，自定义切面逻辑
     */
    private CopyOnWriteArrayList<AopProcessor> aopProcessors = new CopyOnWriteArrayList<>();

    /**
     * 一级缓存，存放经过完整生命周期的单例bean对象
     */
    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();

    /**
     * 二级缓存，存放提前进行aop代理的bean对象
     */
    private ConcurrentHashMap<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();
    /**
     * 三级缓存，存放构造器创建的原始bean对象
     */
    private ConcurrentHashMap<String, Object> singletonFactories = new ConcurrentHashMap<>();
    /**
     * 记录正在创建中的bean，用于判断是否出现循环依赖
     */
    private Set<String> creatingSet = Collections.newSetFromMap(new ConcurrentHashMap<>());
    /**
     * 记录class文件和beanName的映射关系
     */
    private Map<Class<?>, String> classStringMap = new ConcurrentHashMap<>();

    /**
     * 记录接口的默认实现类
     */
    private Map<String, BeanDefinition> interfaceImplMap = new ConcurrentHashMap<>();

    public ApplicationContext() {
    }

    public ApplicationContext(Class<?> configClazz) {
        this.configClazz = configClazz;
        //解析配置文件扫描路径
        reader = new BeanDefinitionReader(configClazz);
        //封装class文件为beanDefinition对象
        beanDefinitionMap = reader.loadBeanDefinitions();
        //        System.out.println("beanDefinitionMap = " + beanDefinitionMap);
        //初始化spring容器
//        refresh();
    }

    public void refresh() {
//        注册所有的BeanPostProcessor,需要提前创建对象
        loadBeanPostProcessors();
//        System.out.println("beanPostProcessors = " + beanPostProcessors);
        //注册所有的AopProcessor，需要提前创建对象
        loadAopProcessor();
//        System.out.println("aopProcessors = " + aopProcessors);
        //注册映射关系
        loadClassStringMap();
//        System.out.println("classStringMap = " + classStringMap);
        loadInterfaceImplMap();
//        System.out.println("interfaceImplMap = " + interfaceImplMap);
        //提前创建所有的bean对象
        doCreate();
        //打印一二级缓存和creatingSet
//        System.out.println("singletonObjects = " + singletonObjects);
//        System.out.println("earlySingletonObjects = " + earlySingletonObjects);
//        System.out.println("singletonFactories = " + singletonFactories);
//        System.out.println("creatingSet = " + creatingSet);
    }

    private void loadInterfaceImplMap() {
        for (String beanName : beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            Class<?> clazz = beanDefinition.getClazz();
            for (Class<?> clazzInterface : clazz.getInterfaces()) {
                interfaceImplMap.put(BeanUtils.getDefaultBeanName(clazzInterface), beanDefinition);
            }
        }
    }

    private void doCreate() {
        for (String beanName : beanDefinitionMap.keySet()) {
            createBean(beanDefinitionMap.get(beanName));
        }
    }

    /**
     * 根据类文件获取bean
     *
     * @param clazz
     * @return
     */
    public <T> T getBean(Class<T> clazz) {
        String beanName = classStringMap.get(clazz);
        return (T) getBean(beanName);

    }

    /**
     * 根据beanName获取对象
     *
     * @param beanName
     * @return 容器中获取的对象
     */
    public Object getBean(String beanName) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        //如果是接口则使用默认实现类
        if (interfaceImplMap.containsKey(beanName)) {
            beanDefinition = interfaceImplMap.get(beanName);
        }
        //是单例则直接从单例池中取
        if (beanDefinition.isSingleton()) {
            return singletonObjects.get(beanDefinition.getBeanName());
        } else {
            //重新创建一个对象
            return createBean(beanDefinition);
        }
    }

    /**
     * 核心功能，根据beanDefinition创建对应的对象
     *
     * @param beanDefinition
     * @return 创建后的对象
     */
    private Object createBean(BeanDefinition beanDefinition) {
        Class<?> clazz = beanDefinition.getClazz();
        String beanName = beanDefinition.getBeanName();
        //如果已经被提前创建则直接返回
        if (singletonObjects.containsKey(beanName) && beanDefinition.isSingleton()) {
            return singletonObjects.get(beanName);
        }
        Object instance = null;
        try {
            //1.构造器创建原始bean
            instance = clazz.getDeclaredConstructor().newInstance();
            //放入creatingSet和三级缓存中
            singletonFactories.put(beanName, instance);
            creatingSet.add(beanName);
            //2.如果不是懒加载则执行依赖注入
            if (!beanDefinition.isLazy()) {
                dependencyInjection(clazz, instance);
            }
            //3.进行Aop
            //先判断是否已经提前aop过
            if (earlySingletonObjects.containsKey(beanName)) {
                instance = earlySingletonObjects.get(beanName);
            } else {
                instance = createBeanAfterDI(beanDefinition);
            }
            //4.放入单例池并清空二级缓存
            singletonObjects.put(beanName, instance);
            earlySingletonObjects.remove(beanName);
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException |
                 NoSuchMethodException e) {
            throw new RuntimeException(e + "创建" + beanName + "对象失败");
        }
        return instance;
    }

    /**
     * 依赖注入
     *
     * @param clazz
     * @param instance
     */
    private void dependencyInjection(Class<?> clazz, Object instance) {
        for (Field declaredField : clazz.getDeclaredFields()) {
            declaredField.setAccessible(true);
            //属性上有依赖注入的注解
            if (declaredField.isAnnotationPresent(AutoWired.class)) {
//                String fieldBeanName = BeanUtils.getDefaultBeanName(declaredField.getType());
                String fieldBeanName = classStringMap.get(declaredField.getType());
                if (fieldBeanName == null) fieldBeanName = BeanUtils.getDefaultBeanName(declaredField.getType());
//                System.out.println("fieldBeanName = " + fieldBeanName);
                AutoWired autoWired = declaredField.getAnnotation(AutoWired.class);
                BeanDefinition fieldBeanDefinition = beanDefinitionMap.get(fieldBeanName);
                declaredField.setAccessible(true);
                try {
                    if (singletonObjects.containsKey(fieldBeanName)) {
                        //从单例池中寻找注入的对象
                        declaredField.set(instance, singletonObjects.get(fieldBeanName));
                    } else if (earlySingletonObjects.containsKey(fieldBeanName)) {
                        //从二级缓存中寻找注入的对象
                        declaredField.set(instance, earlySingletonObjects.get(fieldBeanName));
                    } else {
                        //创建新对象
                        //判断该对象是否正在创建
                        if (!creatingSet.contains(fieldBeanName)) {
                            //没有循环依赖则直接创建对象
                            Object bean = createBean(fieldBeanDefinition);
                            declaredField.set(instance, bean);
                        } else {
                            //出现循环依赖则提前进行aop创建对象
                            Object earlyBean = createBeanAfterDI(fieldBeanDefinition);
                            declaredField.set(instance, earlyBean);
                        }
                    }
                } catch (IllegalAccessException e) {
                    if (autoWired.require()) {
                        throw new RuntimeException("给" + clazz.getSimpleName() + "注入" + fieldBeanName + "失败");
                    }
                }
            }
        }
    }

    /**
     * 进行Aop创建对象
     *
     * @param beanDefinition
     * @return
     */
    private Object createBeanAfterDI(BeanDefinition beanDefinition) {
        String beanName = beanDefinition.getBeanName();
        Class<?> clazz = beanDefinition.getClazz();
        try {
            //从三级缓存中获取原始bean对象
            Object bean = singletonFactories.get(beanName);
            //执行bean初始化前逻辑
            for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
                beanPostProcessor.postProcessBeforeInitialization(bean, beanName);
            }
            //执行自定义初始化
            if (InitializingBean.class.isAssignableFrom(clazz)) {
                InitializingBean initializingBean = (InitializingBean) bean;
                initializingBean.afterPropertiesSet();
            }
            //执行bean初始化前的逻辑
            for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
                beanPostProcessor.postProcessAfterInitialization(bean, beanName);
            }
            //-------------------------AOP入口-----------------------
            //如果该类有接口，且不是提前AOP就使用jdk动态代理
            if (clazz.getInterfaces().length > 0 && singletonFactories.containsKey(beanName)) {
                bean = jdkDynamicProxy(clazz, bean);
            } else {
                //否则使用cglib调用链
                bean = cglibProxy(clazz, bean);
            }
            //向二级缓存添加bean对象
            earlySingletonObjects.put(beanName, bean);
            //删除三级缓存的bean对象和creatingSet
            singletonFactories.remove(beanName);
            creatingSet.remove(beanName);
            return bean;
        } catch (Exception e) {
            throw new RuntimeException(e + beanName + "的Aop处理失败");
        }
    }

    /**
     * cglib无法多重代理，需要转换为拦截器链模式来实现
     *
     * @param clazz
     * @param bean
     * @return
     */
    private Object cglibProxy(Class<?> clazz, Object bean) {
        //没有接口用cglib
        CglibProxy cglibProxy = new CglibProxy(bean);
        //注册所有的Aop,因为cglib不能多重代理
        for (AopProcessor aopProcessor : aopProcessors) {
            String pointCut = aopProcessor.getPointCut();
            AopMatch aopMatch = aopProcessor.getAopMatch();
            if (aopMatch.match(pointCut, clazz.getName())) {
                cglibProxy.registCglibPoint(aopProcessor);
            }
        }
        bean = cglibProxy.getProxyBean();

        return bean;
    }

    private Object jdkDynamicProxy(Class<?> clazz, Object bean) {
        for (AopProcessor aopProcessor : aopProcessors) {
            String pointCut = aopProcessor.getPointCut();
            AopMatch aopMatch = aopProcessor.getAopMatch();
            if (aopMatch.match(pointCut, clazz.getName())) {
                JdkDynamicProxy jdkDynamicProxy = new JdkDynamicProxy(bean, aopProcessor);
                //如果有接口，使用jdk动态代理
                bean = jdkDynamicProxy.getProxyBean();
            }
        }
        return bean;
    }

    /**
     * 注册class和beanName的映射关系
     */
    private void loadClassStringMap() {
        for (String beanName : beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            Class<?> clazz = beanDefinition.getClazz();
            for (Class<?> clazzInterface : clazz.getInterfaces()) {
                classStringMap.put(clazzInterface, beanName);
            }
            classStringMap.put(clazz, beanName);
        }
    }

    /**
     * 注册所有的AopProcessor
     */
    private void loadAopProcessor() {
        for (String beanName : beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            Class<?> clazz = beanDefinition.getClazz();
            if (clazz.isAnnotationPresent(Aspect.class)) {
                AopProcessor aopProcessor = new AopProcessor();
                aopProcessor.setClazz(clazz);
                //获取方法上有关于aop的注解信息
                for (Method declaredMethod : clazz.getDeclaredMethods()) {
                    declaredMethod.setAccessible(true);
                    if (declaredMethod.isAnnotationPresent(PointCut.class)) {
                        PointCut pointCut = declaredMethod.getAnnotation(PointCut.class);
                        String value = pointCut.value();
                        String type = value.substring(0, value.indexOf(':') == -1 ? 0 : value.indexOf(':'));
                        String pointcut = value.substring(value.indexOf(':') + 1);
                        aopProcessor.setPointCut(pointcut);
                        aopProcessor.setAopMatch(AopMatchContext.getAopMatch(type));
                    } else if (declaredMethod.isAnnotationPresent(Before.class)) {
                        aopProcessor.setBeforeMethod(declaredMethod);
                    } else if (declaredMethod.isAnnotationPresent(After.class)) {
                        aopProcessor.setAfterMethod(declaredMethod);
                    }
                }
                aopProcessors.add(aopProcessor);
            }
        }
    }

    /**
     * 注册所有的BeanPostProcessor
     */
    private void loadBeanPostProcessors() {
        for (String beanName : beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            Class<?> clazz = beanDefinition.getClazz();
            if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                try {
                    BeanPostProcessor beanPostProcessor = (BeanPostProcessor) clazz.getDeclaredConstructor().newInstance();
                    beanPostProcessors.add(beanPostProcessor);
                } catch (InstantiationException | NoSuchMethodException | IllegalAccessException |
                         InvocationTargetException e) {
                    throw new RuntimeException("创建BeanPostProcessor对象失败");
                }
            }
        }
    }

    public Map<String, BeanDefinition> getBeanDefinitionMap() {
        return beanDefinitionMap;
    }

    public ConcurrentHashMap<String, Object> getSingletonObjects() {
        return singletonObjects;
    }

    public String[] getBeanNames() {
        return beanDefinitionMap.keySet().toArray(new String[0]);
    }
}
